// Functions to read and parse configuration parameters
// and problem instance to be solved!
// It reads the supplied filename and parses the data
// into a NFS_instance struct.
// pleslie@diku.dk - 2004
#include<iostream>
#include<fstream>
#include <NTL/ZZ.h>
#include <NTL/RR.h>
#include <NTL/ZZX.h>
#include "nfsinput.h"

long LineCount(char* filename ) {
	std::ifstream infile;

	infile.open(filename);
	long lines=0;
	std::string strLine;
	if(infile.good())
	{ while(!infile.eof() && infile.good())
		{ getline(infile,strLine);
			lines++;
		}
	}
	infile.close();
	return lines;
}
	
int Legendre(NTL::ZZ& a, NTL::ZZ& b) {

	NTL::ZZ x;
	NTL::ZZ amodb = a%b; 
	for (x=NTL::to_ZZ(0); x < b; x++) {
		if((x*x-amodb)%b == 0) {
			return 1;
		}
	}
	return -1;

}

bool IsIrreducible(NTL::ZZX f, NTL::ZZ q) {

	int d = NTL::deg(f);
	
	NTL::ZZ res;
	NTL::ZZ tmp;
	NTL::ZZ p;

	for(p=0;p<q;p++) {

		tmp = p;
		res = NTL::coeff(f,0)%q;
		for(int i=1;i<d;i++){
			res+= (NTL::coeff(f,i)%q) * tmp;
			tmp=tmp*tmp;
		}
		res+= NTL::coeff(f,d) * tmp;
		if(res%q != 0) {
			return false;
		}
	}

	return true;
}

NFS_instance readData(char* fname) {

	NFS_instance res;
	
    	std::ifstream infile(fname);
    
	infile >> res.n;
	infile >> res.cofactor;
	infile >> res.primebound;
	infile >> res.rfb_bound;
	infile >> res.afb_bound;
	infile >> res.qcb_num;
	infile >> res.primefile;
	infile >> res.C;

	infile.close();

	return res;
}

void storePoly(nfsPoly poly, char* fname) {
    	std::ofstream outfile(fname);
	outfile << poly.f << std::endl;
	outfile << poly.m << std::endl;
	outfile << poly.alpha << std::endl;
	outfile << poly.rating << std::endl;
	outfile.close();

}

nfsPoly readPoly(char* fname) {
	nfsPoly poly;
    	std::ifstream infile(fname);
	if (infile.good()) {
		infile >> poly.f;
		infile >> poly.m;
		infile >> poly.alpha;
		infile >> poly.rating;
	}
	else {
		poly.alpha=NTL::to_RR(0);
		poly.rating=NTL::to_RR(0);
	}
	infile.close();
	return poly;
}

void printPoly(nfsPoly& poly) {
	std::cout << "-------------------------------" << std::endl;
	std::cout << "- Polynomial                  -" << std::endl;
	std::cout << "-------------------------------" << std::endl;
	std::cout << "f(x): " << poly.f << std::endl;
	std::cout << "m: " << poly.m << std::endl;
	std::cout << "alpha: " << poly.alpha << std::endl;
	std::cout << "rating: " << poly.rating << std::endl;
	std::cout << "-------------------------------" << std::endl;
	
}

NTL::ZZ Evalf(NTL::ZZX& f,NTL::ZZ& j){
	int d = NTL::deg(f);
	NTL::ZZ res = NTL::coeff(f,0);
	NTL::ZZ tmp = j;
	
	for(int i=1;i<d;i++){
		res+= NTL::coeff(f,i) * tmp;
		tmp=tmp*j;
	}
	res+= NTL::coeff(f,d) * tmp;

	return res;
}

NTL::ZZ EvalF(NTL::ZZX f,NTL::ZZ x,NTL::ZZ y ){
	// Evaluate homegenous polynomial F, which is 
	// defined as F(x,y)=y^d*f(x/y)
	int d = NTL::deg(f);
	NTL::ZZ tmpX = NTL::power(x,d); 
	NTL::ZZ res = tmpX*NTL::coeff(f,d);
	NTL::ZZ tmpY = y; 
	
	if(tmpX == NTL::to_ZZ(0) ) {
		return NTL::coeff(f,0);

	}
	tmpX/=x;
	
	for(int i=d-1;i>0;i--){
		res+= NTL::coeff(f,i) * tmpX * tmpY;
		tmpY=tmpY*y;
		tmpX/=x;
	}
	res+= NTL::coeff(f,0) * tmpY;

	return res;
}

NTL::ZZ NormF(NTL::ZZX f,NTL::ZZ x,NTL::ZZ y ){
	// Evaluate norm of x+y*alpha
	// by evaluating homogenous polynomial F, 
	// that is (-y)^d*f(-x/y)
	int d = NTL::deg(f);
	NTL::ZZ tmpX = NTL::power(x,d); 
	NTL::ZZ res = tmpX*NTL::coeff(f,d);
	if (y < NTL::to_ZZ(0)) {
		res= res*NTL::to_ZZ(pow(-1,d));
	}
	NTL::ZZ tmpY = -y; 
	NTL::ZZ y2 = y*NTL::to_ZZ(pow(-1,d)); 
	
	if(tmpX == NTL::to_ZZ(0) ) {
		return NTL::power(y2,d)*NTL::coeff(f,0);

	}
	tmpX/=x;
	
	for(int i=d-1;i>0;i--){
		res+= NTL::coeff(f,i) * tmpX * tmpY;
		tmpY=tmpY*(-y);
		tmpX/=x;
	}
	res+= NTL::coeff(f,0) * tmpY;
	return res;
}

NTL::RR EvalF(NTL::ZZX f,NTL::RR x,NTL::RR y ){
	// Evaluate homegenous polynomial F, which is 
	// defined as F(x,y)=y^d*f(x/y)
	// real variant...
	int d = NTL::deg(f);
	NTL::RR tmpX = NTL::power(x,d); 
	NTL::RR res = tmpX*NTL::MakeRR(NTL::coeff(f,d),0);
	NTL::RR tmpY = y; 
	
	if(tmpX == NTL::to_RR(0) ) {
		return NTL::to_RR(NTL::coeff(f,0));

	}
	tmpX/=x;
	
	for(int i=d-1;i>0;i--){
		res+= NTL::MakeRR(NTL::coeff(f,i),0) * tmpX * tmpY;
		tmpY=tmpY*y;
		tmpX/=x;
	}
	res+= NTL::MakeRR(NTL::coeff(f,0),0) * tmpY;

	return res;
}

NTL::ZZ EvalfmodP(NTL::ZZX& f,NTL::ZZ& j,NTL::ZZ& p){
	int d = NTL::deg(f);
	NTL::ZZ res = NTL::coeff(f,0);
	NTL::ZZ tmp = j;
	
	for(int i=1;i<d;i++){
		res+= NTL::coeff(f,i) * tmp;
		tmp=tmp*j;
		res = res%p;
	}
	res+= NTL::coeff(f,d) * tmp;
	res = res%p;

	return res;
}


